package main

import (
	"fmt"
	"sync"
)

/*
# 简单的
- 作业简单的题:实现一个线程安全的集合set，元素是string
- 要求有 NewSet方法初始化
- Add方法添加元素，添加重复元素可以去重
- Del方法删除元素
- JudgeElement方法 检测输入的string 是否存在于set中
- PrintElement 方法打印所有元素
- Merge方法合并另一个set
- 总之就是set相关的方法
- https://github.com/deckarep/golang-set

*/
//首先声明一个等待组策略的全局变量
var wg sync.WaitGroup

//然后定义集合这个结构体
type SafeSet struct {
	sync.RWMutex                     //因为是线程安全的集合，所以加了读写锁
	m map[string]struct{} //利用map的key不能重复的特性取map的key作为集合的元素，利用struct{}占用的内存最小的特性使其作为map的value
}

//New一个集合的函数
func NewSet() *SafeSet {
	return &SafeSet{
		m: make(map[string]struct{}),
	}
}

//添加元素到集合的结构体方法
func (ss *SafeSet) Add(key string) {
	ss.Lock()
	defer ss.Unlock()
	ss.m[key] = struct{}{}

}

//删除集合元素的结构体方法
func (ss *SafeSet) Del(key string) {
	ss.Lock()
	defer ss.Unlock()
	delete(ss.m, key)
}

//判断元素是否在集合的结构体方法
func (ss *SafeSet) JudgeElement(key string) bool {
	ss.RLock()
	defer ss.RUnlock()
	_, ok := ss.m[key]
	return ok
}

//打印集合元素的结构体方法
func (ss *SafeSet) PrintElement() []string {
	ss.RLock()
	defer ss.RUnlock()
	res := make([]string, 0)
	for k := range ss.m {
		res = append(res, k)
	}
	return res
}

//合并两个集合的结构体方法
func (ss *SafeSet) Merge(ss2 *SafeSet) {
	ss2e := ss2.PrintElement()
	for _, v := range ss2e {
		// ss.Add(v),感觉还是判断有没有这个元素再添加为好，
		// 如果直接添加，就会把之前里面的元素的值给覆盖了，如果万一有值的话
		if !ss.JudgeElement(v) {
			ss.Add(v)
		}
	}

}

//上面有关结构体的初始化和构造方法已经写完
//下面写两个个造数据的方法：
//批量添加元素
func setAddelement(ss *SafeSet, n int) {
	defer wg.Done()
	for i := 1; i <= n; i++ {
		key := fmt.Sprintf("key_%d", i)
		ss.Add(key)
	}

}

//批量删除元素
func setDelelement(ss *SafeSet, n int) {
	defer wg.Done()
	for i := 1; i <= n; i++ {
		key := fmt.Sprintf("key_%d", i)
		ss.Del(key)
	}
}

//准备工作都已经做好，开始进行测试
func main() {
	
	//先初始化一个集合
	s1 := NewSet()
	//下面进行基础测试
	//给这个集合添加了5个元素
	// setAddelement(s1, 5)
	// fmt.Printf("s1集合的元素是：%v\n", s1.PrintElement())
	// s2 := NewSet()
	// setAddelement(s2, 10)
	// fmt.Printf("s2集合的元素是：%v\n", s2.PrintElement())
	// setDelelement(s1, 3)
	// fmt.Printf("s1集合的元素是：%v\n", s1.PrintElement())
	// s1.Merge(s2)
	// fmt.Printf("s1集合的元素是：%v\n", s1.PrintElement())
	//下面进行线程安全的测试：
	wg.Add(5)
	go setAddelement(s1, 10)
	go setDelelement(s1, 5)
	fmt.Printf("s1+集合的元素是：%v\n", s1.PrintElement())
	go setAddelement(s1, 200)
	go setDelelement(s1, 200)
	fmt.Printf("s1++集合的元素是：%v\n", s1.PrintElement())
	go setAddelement(s1, 300)

	wg.Wait()
	// time.Sleep(20 * time.Second)
}
